﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using AsmFromGpp;
using LevenshteinDistance;
using LexicalAnalysisCs;
using TextDiffAlgoCs;

namespace CommandLine
{
    public class Degree
    {
        public double deg1;
        public double deg2;
        public double degMixed;

        public Degree()
        {
            deg1 = -1.0;
            deg2 = -1.0;
            degMixed = -1.0;
        }

        public Degree(double deg1, double deg2, double degMixed)
        {
            this.deg1 = deg1;
            this.deg2 = deg2;
            this.degMixed = degMixed;
        }

        public Degree(Degree deg)
        {
            this.deg1 = deg.deg1;
            this.deg2 = deg.deg2;
            this.degMixed = deg.degMixed;
        }
    }
    public static class CompareManager
    {
        private static void TextDiffCompare(string content1, string content2, ref StringBuilder output, ref List<Degree> degreeList)
        {
            Degree deg = new Degree();
            TextComparer.Compare(content1, content2, ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            degreeList.Add(new Degree(deg));

            output.Append(
                String.Format(
                    "\n--*TextDiff*--\n  +-- \t\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                    deg.deg1 * 100.0, deg.deg2 * 100.0, deg.degMixed * 100.0));
        }

        private static void ListDiffCompare(string content1, string content2, ref StringBuilder output, ref List<Degree> degreeList)
        {
            FileStream fs1 = new FileStream("inFile1.tmp", FileMode.Create);
            StreamWriter sw1 = new StreamWriter(fs1);
            sw1.Write(content1);
            sw1.Close();
            fs1.Close();

            FileStream fs2 = new FileStream("inFile2.tmp", FileMode.Create);
            StreamWriter sw2 = new StreamWriter(fs2);
            sw2.Write(content2);
            sw2.Close();
            fs2.Close();

            Degree deg = new Degree();
            LexicalAnalyzerCs lac1 = new LexicalAnalyzerCs("inFile1.tmp", "outFile1.tmp");
            LexicalAnalyzerCs lac2 = new LexicalAnalyzerCs("inFile2.tmp", "outFile2.tmp");

            output.Append("\n--*ListDiff*--\n");
            List<LexicalAnalyzerCs.StrInt> includeList1 = lac1.GetIncludeFileNames(true);
            List<LexicalAnalyzerCs.StrInt> includeList2 = lac2.GetIncludeFileNames(true);
            List<TextDiff> includeDiffList = ListComparer.Compare(includeList1, includeList2,
                                                                  ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            output.Append(
                String.Format("  *-- Headers:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                              deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            degreeList.Add(new Degree(deg));

            List<LexicalAnalyzerCs.StrInt> namespaceList1 = lac1.GetNamespaceName(true);
            List<LexicalAnalyzerCs.StrInt> namespaceList2 = lac2.GetNamespaceName(true);
            List<TextDiff> namespaceDiffList = ListComparer.Compare(namespaceList1, namespaceList2,
                                                                    ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            output.Append(
                String.Format("  *-- Using Namespaces:\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                              deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            degreeList.Add(new Degree(deg));

            List<LexicalAnalyzerCs.StrInt> wordsList1 = new List<LexicalAnalyzerCs.StrInt>();
            List<LexicalAnalyzerCs.StrInt> wordsList2 = new List<LexicalAnalyzerCs.StrInt>();
            List<LexicalAnalyzerCs.StrInt> symbolList1 = new List<LexicalAnalyzerCs.StrInt>();
            List<LexicalAnalyzerCs.StrInt> symbolList2 = new List<LexicalAnalyzerCs.StrInt>();
            lac1.GetWordsSymbolsList(ref wordsList1, ref symbolList1);
            lac2.GetWordsSymbolsList(ref wordsList2, ref symbolList2);

            wordsList1.Sort();
            wordsList2.Sort();
            symbolList1.Sort();
            symbolList2.Sort();

            List<TextDiff> wordsDiffList = ListComparer.Compare(wordsList1, wordsList2,
                                                                ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            output.Append(
                String.Format("  *-- Words:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                              deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            degreeList.Add(new Degree(deg));

            List<TextDiff> symbolDiffList = ListComparer.Compare(symbolList1, symbolList2,
                                                                 ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            output.Append(
                String.Format("  *-- Symbols:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                              deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            degreeList.Add(new Degree(deg));

            List<LexicalAnalyzerCs.StrInt> resList1 = lac1.getResList;
            List<LexicalAnalyzerCs.StrInt> resList2 = lac2.getResList;
            List<TextDiff> diffList = ListComparer.Compare(resList1, resList2,
                                                           ref deg.deg1, ref deg.deg2, ref deg.degMixed);
            output.Append(
                String.Format("  *-- Body:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                              deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            degreeList.Add(new Degree(deg));

//            try
//            {
                File.Delete(@"inFile1.tmp");
                File.Delete(@"inFile2.tmp");
                File.Delete(@"outFile1.tmp");
                File.Delete(@"outFile2.tmp");
//            } 
//            catch (Exception ex)
//            {
//                output.Append("Exception: " + ex.Message + "\n");
//            }
        }

        private static void AsmCompare(string content1, string content2, ref StringBuilder output, ref List<Degree> degreeList)
        {
            output.Append(
                String.Format("\n--*Asm Compare*--\n"));

            FileStream fs1 = new FileStream("RichTextBox1.cpp", FileMode.Create);
            StreamWriter sw1 = new StreamWriter(fs1);
            sw1.Write(content1 + '\n');
            sw1.Close();
            fs1.Close();

            FileStream fs2 = new FileStream("RichTextBox2.cpp", FileMode.Create);
            StreamWriter sw2 = new StreamWriter(fs2);
            sw2.Write(content2 + '\n');
            sw2.Close();
            fs2.Close();

            CodeDecorating cd1 = new CodeDecorating("RichTextBox1.cpp", "rtb1Lex.out");
            CodeDecorating cd2 = new CodeDecorating("RichTextBox2.cpp", "rtb2Lex.out");
            cd1.InsertInlines();
            cd2.InsertInlines();
            cd1.OutputCodesToFile("RichTextBox1.cpp");
            cd2.OutputCodesToFile("RichTextBox2.cpp");

            GenerateAsm ga1 = new GenerateAsm();
            GenerateAsm ga2 = new GenerateAsm();
            int flag = 0;

            try
            {
                ga1.GenAsmToFile("RichTextBox1.cpp");
                ga1.GenInstructionList();
                flag++;
            }
            catch
            {
                output.Append(
                    String.Format(
                        "--*Asm Box1 Generated Crushed, might be the reason that the code cannot pass the compilation*--\n"));
            }

            try
            {
                ga2.GenAsmToFile("RichTextBox2.cpp");
                ga2.GenInstructionList();
                flag++;
            }
            catch
            {
                output.Append(
                    String.Format(
                        "--*Asm Box2 Generated Crushed, might be the reason that the code cannot pass the compilation*--\n"));
            }

            if (flag == 2)
            {
//                Console.WriteLine("--*Asm Generated Completed*--");
                Degree deg = new Degree();
                ListComparer.Compare(ga1.MAsmOperandList, ga2.MAsmOperandList,
                                     ref deg.deg1, ref deg.deg2, ref deg.degMixed);
                degreeList.Add(new Degree(deg));
                output.Append(
                    String.Format("  +-- Operand:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                                  deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));

                ListComparer.Compare(ga1.MAsmInstList, ga2.MAsmInstList,
                                     ref deg.deg1, ref deg.deg2, ref deg.degMixed);
                degreeList.Add(new Degree(deg));
                output.Append(
                    String.Format("  +-- Instruction:\t\tDegree1: {0:N3}% Degree2: {1:N3}% DegreeMixed: {2:N3}%\n",
                                  deg.deg1*100.0, deg.deg2*100.0, deg.degMixed*100.0));
            } else
            {
                degreeList.Add(new Degree(-1, -1, -1));
                degreeList.Add(new Degree(-1, -1, -1));
            }

            File.Delete("RichTextBox1.cpp");
            File.Delete("RichTextBox2.cpp");
            File.Delete("rtbLex1.out");
            File.Delete("rtbLex2.out");
        }

        public static List<Degree> Compare(string filePath1, string filePath2, ref StringBuilder output)          // return the similarity degree list;
        {
            output.Append(
                String.Format("File 01: {0}\t", filePath1));
            output.Append(
                String.Format("File 02: {0}\n", filePath2));
            if (!File.Exists(filePath1) || !File.Exists(filePath2))
            {
                output.Append(
                    String.Format("  At least one of the files doesn't exist."));
                return null;
            }

            string content1 = "", content2 = "";
            List<Degree> degreeList = new List<Degree>();
            FileStream fsRawContent1 = new FileStream(filePath1,  FileMode.Open);
            FileStream fsRawContent2 = new FileStream(filePath2, FileMode.Open);
            StreamReader srRawContent1 = new StreamReader(fsRawContent1);
            StreamReader srRawContent2 = new StreamReader(fsRawContent2);

            content1 = srRawContent1.ReadToEnd();
            content2 = srRawContent2.ReadToEnd();

            srRawContent1.Close();
            srRawContent2.Close();
            fsRawContent1.Close();
            fsRawContent2.Close();

//             double simDeg = Levenshtein.SimilarityDegree(content1, content2);
//             output.Append(
//                 String.Format(simDeg.ToString()));
//             if (simDeg < 0.6)
//             {
//                 Console.WriteLine("Not similar.");
//                 return null;
//             }

            TextDiffCompare(content1, content2, ref output, ref degreeList);
            ListDiffCompare(content1, content2, ref output, ref degreeList);
            AsmCompare(content1, content2, ref output, ref degreeList);
            output.Append("\n");

//             Console.WriteLine();
//             foreach (var d in degreeList)
//             {
//                 Console.WriteLine("{0}, {1}, {2}", d.deg1, d.deg2, d.degMixed);
//             }

            return degreeList;
        }
    }
}
